
module sqlite3d.Data;

import 
	sqlite3d.pub;

char[] WrapMixin(char[] src){
	return "//:" ~ ctfe2_i2a(src.length) ~ ":\n" ~ src;
}

private char[] DefEnumOfStorageClasses(char[] Enum)(char[][] Ary){
	char[]	src	= "enum " ~ Enum ~ " : int {\n";
	foreach(int i,  c;  Ary){
		src	~=  "\t" ~ c ~ ", \n";
	}
	src	~= "}";
	return WrapMixin(src);
}

static const char[][] StorageClasses	= ["NULL", "INTEGER", "REAL", "TEXT", "BLOB"];
mixin(DefEnumOfStorageClasses!("StorageClass"c)(StorageClasses) );

struct MetaDataType{
	const StorageClass			Type;
	const char[]				DType;
	const char[]				AType;
}

static const MetaDataType[] MetaDataTypes	= [
		{StorageClass.NULL,		"void*",	"NULL"	},
		{StorageClass.INTEGER,	"int",		"INT"		},
		{StorageClass.INTEGER,	"uint",	"UINT"	},
		{StorageClass.INTEGER,	"long",	"INT64"	},
		{StorageClass.INTEGER,	"ulong",	"UINT64"	},
		{StorageClass.REAL,		"float",	"REAL"	},
		{StorageClass.TEXT,		"char[]",	"TEXT"	},
		{StorageClass.TEXT,		"wchar[]",	"TEXT16"	},
		{StorageClass.BLOB,		"void[]",	"BLOB"	},
		{StorageClass.BLOB,		"void*",	"USER"	},
	];

private char[] DefEnumOfMetaDataTypes(char[] Enum)(MetaDataType[] Ary){
	char[]	src	= "enum " ~ Enum ~ " : int {\n";
	foreach(int i, c;  Ary){
		src	~=  "\t" ~ c.AType ~ ", \n";
	}
	src	~= "}";
	return WrapMixin(src);
}

version(PRINT_MIXIN) pragma(msg, DefEnumOfMetaDataTypes!("DataType")(MetaDataTypes));
mixin(DefEnumOfMetaDataTypes!("DataType")(MetaDataTypes));

private char[] DefAliasOfMetaDataTypes(char[] pre)(MetaDataType[] Ary){
	char[]	src;
	foreach(int i, c;  Ary){
		src	~=  "alias " ~ c.DType ~ "\t" ~  pre ~ c.AType ~ ";\n";
	}
	return WrapMixin(src);
}

version(PRINT_MIXIN) pragma(msg, DefAliasOfMetaDataTypes!("DataType_")(MetaDataTypes));
mixin(DefAliasOfMetaDataTypes!("DataType_")(MetaDataTypes));

static const char[][] Affinities	= ["TEXT",	"NUMERIC",	"INTEGER",	"REAL", "NONE" ];

static const char[][] Collations	= ["BINARY", 	"NOCASE", 	"RTRIM"];

struct ColumnConstrain{
	char[]	DType;
	char[]	Desc;
}


static const ColumnConstrain[]	ColumnConstrains	= [
		{"NULL",		"NULL"			},
		{"ROWID",		"PRIMARY KEY"		},
		{"AUTO",		"AUTOINCREMENT"	},
		{"UNIQUE",		"UNIQUE"			},
		{"NOT_NULL",	"NOT NULL"			},
		{"INDEX",		""				},
	];

private char[] DefEnumOfColumnConstrains(char[] Enum)(ColumnConstrain[] Ary){
	char[]	src	= "enum " ~ Enum ~ " : int {\n";
	foreach(int i, c;  Ary){
		src	~=  "\t" ~ c.DType ~ "\t= " ~ ctfe2_i2a(Pow2a[i]) ~ ", \n";
	}
	src	~= "\tID\t= ROWID | AUTO, \n";
	src	~= "}\n";
	return WrapMixin(src);
}

version(PRINT_MIXIN) pragma(msg, DefEnumOfColumnConstrains!("ColType")(ColumnConstrains));
mixin(DefEnumOfColumnConstrains!("ColType")(ColumnConstrains));

struct ColumnDef{
	const DataType	type;
	const char[]	name;
	const int		col_type;
}

struct TableDef{
	const char[] 		name;
	const ColumnDef[]	cols;
}

public char[] DefTableCols(alias T)(){
	char[] src = "enum : ubyte  {\n";
	foreach(int i  , c; T.def.cols){
		src	~=  "\t i_" ~ c.name ~ ",\n";
	}
	src	~= "}";
	return WrapMixin(src);
}

public char[] DefTableCols2(alias T)(){
	char[] src = "enum : int  {\n";
	foreach(int i  , c; T.def.cols){
		src	~=  "\t _" ~ c.name  ~ " = " ~  ctfe2_i2a(Pow2a[i]) ~ ",\n";
	}
	src	~= "}";
	return WrapMixin(src);
}

public char[] DefTableCreate(TableDef def){
	char[] sql	= "CREATE TABLE  IF NOT EXISTS  " ~ def.name ~ "( \n";
	foreach(int i, col; def.cols ){
			int ii	= MetaDataTypes[col.type].Type;
			char[] pre	= "\t" ~ col.name ~ " " ~ StorageClasses[ii] ~ " ";
			foreach(int _i , c; ColumnConstrains){
				bool b	= ( Pow2a[_i] & col.col_type) is Pow2a[_i]  ;
				if( b ){
					pre	~= c.Desc ~ " ";
				}
			}
			sql	~=  pre  ~  ",\n";
	}
	sql	= sql[0..$-2];
	sql	~= "\n);";
	return sql;
}

public int TableRowId(TableDef def){
	static assert( is( typeof(def) == TableDef ) , T.stringof ~ " is not type of TableDef");
	int rid	= -1;
	foreach(int i, col; def.cols ){
		foreach(int _i , c; ColumnConstrains){
			bool b	= ( Pow2a[_i] & col.col_type) is ColType.ROWID  ;
			if( b ){
				return i;
			}
		}
	}
	return rid;
}

template DefTable(alias T){
	static assert( is( typeof(T.def) == TableDef ) , T.stringof ~ " is not type of TableDef");
	version(PRINT_MIXIN) pragma(msg, cast(char[]) DefTableCols!(T));
	mixin(DefTableCols!(T));
	version(PRINT_MIXIN) pragma(msg, cast(char[]) DefTableCols2!(T));
	mixin(DefTableCols2!(T));
	static const int rowid			= TableRowId(T.def);
	static if( rowid >= 0 ){
		static assert( (T.def.cols[rowid].type is DataType.INT) || (T.def.cols[rowid].type is DataType.UINT) );
	}
	static const char[] sql_create	= DefTableCreate(T.def);
	alias T Self;
}

enum QueryType : ubyte{
	SELECT,
	INSERT,
	UPDATE,
	DELETE
}

int TrueMask(alias T, int mask)(){
	int _mask;
	foreach(int j, col; T.def.cols ){
		if( ( Pow2a[j] & mask ) is Pow2a[j]  ){
			_mask	|= Pow2a[j] ;
		}
	}
	return _mask;
}

char[]  DefMaskStruct(alias T, char[] name, int mask)(){
	char[] src		= "struct Row_" ~ name ~ " {\n";
	
	bool isUseRowId ;
	int oid		= TableRowId(T.def);
	if( oid >= 0 ){
		isUseRowId	= ( mask & Pow2a[oid] ) is Pow2a[oid];
		src	~= "\t" ~ MetaDataTypes[T.def.cols[oid].type].DType ~ "\t" ~ T.def.cols[oid].name  ~ " ;\n";
	} else {
		src	~= "\tint\tROWID;\n";
	}
	
	foreach(int j, col; T.def.cols ){
		if( j !is oid){
			if( ( Pow2a[j] & mask ) is Pow2a[j]  ){
				src	~= "\t" ~ MetaDataTypes[col.type].DType ~ "\t" ~ col.name  ~ " ;\n";
			}
		}
	}
	src	~= "\talias def\ttable_def ;\n";
	if( oid >= 0 ){
		src	~= "\talias " ~ T.def.cols[oid].name ~ " ROWID;\n";
	}
	src	~= "\tstatic const int\tmask	=  " ~ ctfe2_i2a(mask) ~ " ;\n";
	src	~= "\tstatic const int[]\tmasks	= [ " ;
	if( oid >= 0 ){
		src	~= ctfe2_i2a(oid) ~ ",";
	}else{
		src	~= "-1,";
	}
	foreach(int j, col; T.def.cols ){
		if( j !is oid){
			if( ( Pow2a[j] & mask ) is Pow2a[j]  ){
				src	~= ctfe2_i2a(j) ~ ",";
			}
		}
	}
	src	= src[0..$-1] ~ " ] ;\n";
	src	~= "\tmixin DefaultOperator!(Row_" ~ name~ ");\n";
	src	~= "}\n";
	return WrapMixin(src);
}

template DefMask(alias T, M...){
	static assert( is(typeof(T.def) == TableDef) );
	static assert( M.length > 1 );
	static assert( is( ElementTypeOfArray!(typeof(M[0])) ==  char) );
	static assert( is( typeof(M[1]) ==  int) );
	
	static if( TrueMask!(T, M[1]) > 0 ){
		version(PRINT_MIXIN) 
			pragma(msg, cast(char[]) DefMaskStruct!(T, M[0],  cast(int) TrueMask!(T, M[1]) ) );
		mixin( DefMaskStruct!(T, M[0],  cast(int) TrueMask!(T, M[1]) ) );
	}
	static if(M.length > 2 ){
		mixin DefMask!(T, M[2..$]);
	}
}
